package ch.ethz.fcl.metrobuzz.data.loader.raw;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import ch.ethz.fcl.metrobuzz.MBProperty;
import ch.ethz.fcl.metrobuzz.algorithm.network.shortest_path.public_transport.PathPublic;
import ch.ethz.fcl.metrobuzz.data.MBData;
import ch.ethz.fcl.metrobuzz.data.loader.PublicTransitType;
import ch.ethz.fcl.metrobuzz.data.scene.basemap.Node;
import ch.ethz.fcl.metrobuzz.data.scene.spacetime.Agent;
import ch.ethz.fcl.metrobuzz.data.scene.spacetime.Itinerary;
import ch.ethz.fcl.metrobuzz.data.transit.Stop;
import ch.ethz.fcl.metrobuzz.data.transit.TransitLine;
import ch.ethz.fcl.metrobuzz.data.transit.TransitRoute;
import ch.ethz.fcl.metrobuzz.tools.MBTools;

/**
 * Load raw EZLink data
 * 
 * @author Zeng Wei
 * @email zeng@arch.ethz.ch
 * 
 */
public class RawEZLinkLoader {
	private int loadTripLimit = 100000000;

	// determine if and how much data should be sampled.
	private final boolean SAMPLE_DATA = false;
	private final float SAMPLE_RATE = 0.1F;

	FileWriter fw = null;
	BufferedWriter bw = null;
	Map<Long, Integer> loadedCardMap;
	int sampledTrips = 0;

	@SuppressWarnings("resource")
	public void load(MBData data, String filePath) {
		int numOfTrips = 0;
		long time = System.currentTimeMillis();

		try {
			BufferedReader reader = new BufferedReader(new FileReader(filePath));
			reader.readLine();// first line is skipped
			String tripEntry = null;

			if (SAMPLE_DATA) {
				loadedCardMap = new HashMap<Long, Integer>();
				File file = new File("res/Singapore/sampled_raw_data");

				// if file doesn't exists, then create it
				if (!file.exists()) {
					file.createNewFile();
				}

				fw = new FileWriter(file.getAbsoluteFile());
				bw = new BufferedWriter(fw);
			}

			while ((tripEntry = reader.readLine()) != null
					&& numOfTrips < loadTripLimit) {
				inputProcess(data, tripEntry);
				numOfTrips++;
			}

			if (SAMPLE_DATA) {
				bw.flush();
				bw.close();
				fw.flush();
				fw.close();
			}
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (Exception e) {
			e.printStackTrace();
		}

		if (SAMPLE_DATA)
			System.out.println(sampledTrips + " Trips sampled");
		System.out.println("Total " + numOfTrips + " trip entries loaded in "
				+ (System.currentTimeMillis() - time) / 1000.0f + " secs");
	}

	private void inputProcess(MBData data, String input) throws Exception {
		String[] fields = input.split(",");

		if (fields.length != 15) {
			throw new Exception("Wrong input fields");
		}

		long tripId = Long.parseLong(fields[0]); // trip id
		long cardId = Long.parseLong(fields[1]); // agent/card id

		if (SAMPLE_DATA) {
			if (loadedCardMap.get(cardId) != null) {
				bw.write(input + "\n");
				sampledTrips++;
			} else {
				if (Math.random() < SAMPLE_RATE) {
					bw.write(input + "\n");
					loadedCardMap.put(cardId, 1);
					sampledTrips++;
				}
			}
		}

		Stop boardStop, alightStop;
		PublicTransitType type;
		if (fields[3].equals("RTS"))
			type = PublicTransitType.RTS;
		else if (fields[3].equals("Bus"))
			type = PublicTransitType.BUS;
		else {
			throw new Exception("Unknown trip type");
		}

		// to accelerate the loading speed when it is MRT trip
		if (type == PublicTransitType.RTS && MBProperty.ACCESSIBILITY)
			return;

		String boardStopName = fields[7].replace("STN ", "");
		String alightStopName = fields[8].replace("STN ", "");

		// some people take wrong bus
		// 102465695383,9000238562880800,Adult,Bus,199
		// ,1,8121,22009,22009,11/04/2011,20:32:13.000000,0.100,4.100,0.02,2
		if (boardStopName.endsWith(alightStopName))
			return;

		float startTime = MBTools.convertTimeFromString(fields[10]);
		float travelTime = -1;

		// if people don't tap out, ignore this field
		if (!fields[12].equals("?")) {
			travelTime = Float.parseFloat(fields[12]) * 60;
		} else
			return;

		int transferNum = Integer.parseInt(fields[14]);

		Agent agent = data.getScene().getAgentMap().get(cardId);
		Itinerary itinerary = null;

		if (MBProperty.OD) {
			if (agent == null) {
				agent = new Agent(cardId);
				data.getScene().addAgent(cardId, agent);
				itinerary = new Itinerary(agent);
			} else
				itinerary = agent.getItinerary();
		}

		if (type == PublicTransitType.BUS) {
			// field 4 is transit line number, 5 is direction (0/1), 6 is
			// registration number
			TransitLine transit = data.getPublicTransit().getLine(
					fields[4].trim());

			if (transit == null)
				return;

			int direction = Integer.parseInt(fields[5]);
			int busRegNum = Integer.parseInt(fields[6]);

			if (boardStopName.length() < 5)
				boardStopName = "0" + boardStopName;
			if (alightStopName.length() < 5)
				alightStopName = "0" + alightStopName;
			boardStop = data.getPublicTransit().getStopByStopId(boardStopName);
			alightStop = data.getPublicTransit()
					.getStopByStopId(alightStopName);

			if (boardStop == null)
				return;

			if (alightStop == null)
				return;

			if (transit != null
					// these transit lines does not match the transit schedule
					&& !transit.getId().equals("549")
					&& !transit.getId().equals("11")
					&& !transit.getId().endsWith("552")
					&& !transit.getId().equals("185")
					&& !transit.getId().equals("24")) {
				if (travelTime != -1) {
					TransitRoute route = transit.getRoute(direction, boardStop,
							alightStop);

					if (route != null) {
						if (MBProperty.OD) {
							List<Integer> nodeIndices = itinerary
									.getNodeIndices(data, startTime, startTime
											+ travelTime, route, boardStop,
											alightStop);

							itinerary.addTrip(data, nodeIndices, tripId,
									transferNum, startTime, startTime
											+ travelTime, 1);
						} else if (MBProperty.ACCESSIBILITY) {
							// System.out.println("Route " + route.getId()
							// + " add bus " + busRegNum + " start "
							// + boardStop.getId() + " alight "
							// + alightStop.getId());
							route.passengerTap(input, busRegNum, boardStop,
									startTime, alightStop, startTime
											+ travelTime);
						}
					}
				}
			}
		}
		// if it is MRT trips and origin-destination visualization
		else if (type == PublicTransitType.RTS && MBProperty.OD) {
			boardStop = data.getPublicTransit()
					.getStopByStopName(boardStopName);
			alightStop = data.getPublicTransit().getStopByStopName(
					alightStopName);
				
			PathPublic p = data.getAllToAllDijkstra().getPath(boardStop,
					alightStop);

			if (p != null && travelTime != -1) {
				List<Integer> nodeIndices = new ArrayList<Integer>();

				for (Node n : p.getAllNodes())
					nodeIndices.add(data.getScene().getNodeIndex(n.getId()));

				itinerary.addTrip(data, nodeIndices, tripId, transferNum,
						startTime, startTime + travelTime, 1);
			}
		}
	}
}
